package com.tbit.main.service.impl;

import com.tbit.main.service.RedisService;
import com.tbit.main.util.DateTimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.stereotype.Service;
import redis.clients.util.JedisByteHashMap;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service("redisService")
public class RedisServiceImpl implements RedisService {

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    @Qualifier("redisTemplate1")
    private RedisTemplate<String, String> redisTemplate1;

    @Override
    public void add(String key, String value) {
        ValueOperations<String, String> valueOperation = redisTemplate.opsForValue();
        valueOperation.set(key, value);
    }

    @Override
    public void add(String key, String value, long timeout, TimeUnit unit) {
        ValueOperations<String, String> valueOperation = redisTemplate.opsForValue();
        valueOperation.set(key, value, timeout, unit);
    }

    @Override
    public String get(String key) {
        ValueOperations<String, String> valueOperation = redisTemplate.opsForValue();
        String value = null;
        try {
            value = valueOperation.get(key);
        } catch (SerializationException e) {
            del(key);
        }
        return value;
    }

    @Override
    public String get(String hashId, String key) {
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        return hashOperations.get(hashId, key);
    }

    @Override
    public String getGetWay(String hashId, String key) {
        HashOperations<String, String, String> hashOperations = redisTemplate1.opsForHash();
        return hashOperations.get(hashId, key);
    }

    @Override
    public String get1(String key) {
        ValueOperations<String, String> valueOperation = redisTemplate1.opsForValue();
        return valueOperation.get(key);
    }

    @Override
    public Map<String, String> getAll(String hashId) {
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();

        return hashOperations.entries(hashId);
    }

    @Override
    public Map<String, Map<String, String>> getByHashIds(final List<String> hashIds) {
        Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();

        List<Object> objects = getKeys(hashIds);
        for (int i = 0; i < objects.size(); i++) {
            Object object = objects.get(i);
            if (object != null) {
                HashMap<String, String> resultMap = Hash_FormatResult((JedisByteHashMap) object);
                result.put(hashIds.get(i), resultMap);
            }
        }

        return result;
    }


    /**
     * 管道批量获取
     */
    @SuppressWarnings({"rawtypes"})
    private List<Object> getKeys(final List<String> keys) {
        final RedisSerializer hashIdSer = redisTemplate.getKeySerializer();
        final List<Object> result = redisTemplate.execute(new RedisCallback<List<Object>>() {
            @SuppressWarnings("unchecked")
            public List<Object> doInRedis(RedisConnection connection) throws DataAccessException {
                connection.openPipeline();
                for (String keyStr : keys) {
                    byte[] key = hashIdSer.serialize(keyStr);
                    connection.hGetAll(key);
                }
                return connection.closePipeline();
            }
        });

        return result;
    }

    /**
     * 解析
     */
    @SuppressWarnings("rawtypes")
    private HashMap<String, String> Hash_FormatResult(JedisByteHashMap byteHashMap) {
        final RedisSerializer proSer = redisTemplate.getHashKeySerializer();
        final RedisSerializer valueSer = redisTemplate.getHashValueSerializer();
        Set<Map.Entry<byte[], byte[]>> set = byteHashMap.entrySet();
        HashMap<String, String> resulHash = new HashMap<>();
        for (Map.Entry<byte[], byte[]> kv : set) {
            resulHash.put(proSer.deserialize(kv.getKey()).toString(), valueSer.deserialize(kv.getValue()).toString());
        }
        return resulHash;
    }


    @Override
    public void add(String key, String value, Integer seconds) {
        ValueOperations<String, String> valueOperation = redisTemplate.opsForValue();
        valueOperation.set(key, value, seconds, TimeUnit.SECONDS);
    }

    @Override
    public void del(String key) {
        redisTemplate.delete(key);
    }

    @Override
    public void set(String hashId, String key, String value) {
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        hashOperations.put(hashId, key, value);
    }

    @Override
    public void andToList(String key, String value) {
        ListOperations<String, String> listOpreations = redisTemplate.opsForList();
        listOpreations.rightPush(key, value);
    }

    @Override
    public void andList(String key, List<String> value) {
        for (String v : value) {
            ListOperations<String, String> listOpreations = redisTemplate.opsForList();
            listOpreations.rightPush(key, v);
        }
    }

    @Override
    public List<String> getList(String key) {

        List<String> values = null;
        try {
            ListOperations<String, String> listOpreations = redisTemplate.opsForList();
            values = listOpreations.range(key, 0, -1);
        } catch (SerializationException e) {
            del(key);
        }

        return values;
    }

    @Override
    public void del(String hashId, String key) {
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(hashId, key);
    }

    @Override
    public void delToList(String key, String value) {
        ListOperations<String, String> listOpreations = redisTemplate.opsForList();
        listOpreations.remove(key, -1, value);
    }

    @Override
    public Boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }

    @Override
    public boolean locked(String key, Integer second) {
        if (hasKey(key)) {
            return true;
        }
        add(key, "1", second, TimeUnit.SECONDS);
        return false;
    }

    @Override
    public boolean setNx(String key, String value, long timeout) {
        try {
            Boolean tag = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
            //获取锁成功
            return tag != null && tag;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public void incr(String hashId) {
        if (get(DateTimeUtil.getNowTimeYMD() + ".MAIN" + hashId) == null) {
            add(DateTimeUtil.getNowTimeYMD() + ".MAIN" + hashId, "1", 60 * 60 * 3);
            return;
        } else {
            int count = Integer.parseInt(get(DateTimeUtil.getNowTimeYMD() + ".MAIN" + hashId)) + 1;
            add(DateTimeUtil.getNowTimeYMD() + ".MAIN" + hashId, "" + count, 60 * 60 * 3);
        }
    }

    @Override
    public Set<String> getLikeKeys(String keys) {
        // 模糊查找所有的key
        Set<String> keyLike = redisTemplate.keys(keys);
        return keyLike;
    }

    @Override
    public void delLikeKeys(Set<String> keyLike) {
        // 批量删除
        redisTemplate.delete(keyLike);
    }


    /**
     * HashSet
     *
     * @param key 键
     * @param map 对应多个键值
     */
    @Override
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
    }

    /**
     * HashSet 并设置时间
     *
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    @Override
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
    }

    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     * @return
     */
    @Override
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
    }

    /**
     * 获取hashKey对应的所有键值
     *
     * @param key 键
     * @return 对应的多个键值
     */
    @Override
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }
}
